{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "def createTrainDatasWithNoise(W, start, end, size = 10, noise = 0.05):\n",
    "    \"\"\"\n",
    "    创建带噪声的训练数据集\n",
    "    args:\n",
    "        W - 目标权重系数\n",
    "        start - 最小值\n",
    "        end - 最大值\n",
    "        size - 训练数据集大小\n",
    "        noise - 噪音大小\n",
    "    return:\n",
    "        X - 训练集特征值\n",
    "        y - 训练集目标值\n",
    "    \"\"\"\n",
    "    np.random.seed(42)\n",
    "    X = np.random.uniform(start, end, (size, W.shape[0]))\n",
    "    y = []\n",
    "    for index in range(len(X)):\n",
    "        if np.random.random() > noise:\n",
    "            y.append(np.sign(X[index].dot(W)))\n",
    "        else:\n",
    "            y.append(-np.sign(X[index].dot(W)))\n",
    "    return X, np.array(y)\n",
    "\n",
    "def buildLine(W, start, end):\n",
    "    \"\"\"\n",
    "    构建一条指定的二维直线\n",
    "    args:\n",
    "        W - 权重系数\n",
    "        start - 最小值\n",
    "        end - 最大值\n",
    "        size - 组成线的点的数量\n",
    "    return:\n",
    "        x0 - x0集合\n",
    "        x1 - x1集合\n",
    "    \"\"\"\n",
    "    x0 = np.linspace(start, end, size)\n",
    "    if W[1] == 0:\n",
    "        x1 = np.zeros(100)\n",
    "    else:\n",
    "        x1 = -W[0] * x0 / W[1]\n",
    "    return x0, x1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "创建带噪声的数据集："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 坐标轴起始点\n",
    "start = -10\n",
    "# 坐标轴结束点\n",
    "end = 10\n",
    "# 目标权重系数\n",
    "W = np.array([2, 5])\n",
    "# 创建线性可分的训练数据集\n",
    "X, y = createTrainDatasWithNoise(W, start, end)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可视化："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "from mpl_toolkits.mplot3d import Axes3D\n",
    "\n",
    "plt.rcParams['font.sans-serif'] = ['PingFang HK']  # 选择一个本地的支持中文的字体\n",
    "\n",
    "# %matplotlib inline\n",
    "\n",
    "x1 = X[y>0][:, 0]\n",
    "y1 = X[y>0][:, 1]\n",
    "x2 = X[y<0][:, 0]\n",
    "y2 = X[y<0][:, 1]\n",
    "plt.title('口袋算法')\n",
    "p1 = plt.scatter(x1, y1, c='b', marker='o', s=20)\n",
    "p2 = plt.scatter(x2, y2, c='r', marker='o', s=20)\n",
    "x3, y3 = buildLine(W, start, end)\n",
    "plt.plot(x3, y3)\n",
    "plt.legend([p1, p2], [\"正1\", \"负1\"], loc=\"upper right\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用 Python 实现口袋算法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "def errorIndexes(W, X, y):\n",
    "    \"\"\"\n",
    "    获取错误点的下标集合\n",
    "    args:\n",
    "        W - 权重系数\n",
    "        X - 训练数据集\n",
    "        y - 目标标签值\n",
    "    return:\n",
    "        errorIndexes - 错误点的下标集合\n",
    "    \"\"\"\n",
    "    errorIndexes = []\n",
    "    # 遍历训练数据集\n",
    "    for index in range(len(X)):\n",
    "        x = X[index]\n",
    "        # 判定是否与目标值不符\n",
    "        if x.dot(W) * y[index] <= 0:\n",
    "            errorIndexes.append(index)\n",
    "    return errorIndexes\n",
    "\n",
    "def pocket(X, y, iteration, maxIterNoChange = 10):\n",
    "    \"\"\"\n",
    "    口袋算法实现\n",
    "    args:\n",
    "        X - 训练数据集\n",
    "        y - 目标标签值\n",
    "        iteration - 最大迭代次数\n",
    "    return:\n",
    "        W - 权重系数\n",
    "    \"\"\"\n",
    "    np.random.seed(42)\n",
    "    # 初始化权重系数\n",
    "    W = np.zeros(X.shape[1])\n",
    "    # 获取错误点的下标集合\n",
    "    errors = errorIndexes(W, X, y)\n",
    "    iterNoChange = 0\n",
    "    # 循环\n",
    "    for i in range(iteration):\n",
    "        iterNoChange = iterNoChange + 1\n",
    "        # 随机获取错误点下标\n",
    "        errorIndex = np.random.randint(0, len(errors))\n",
    "        # 计算临时权重系数\n",
    "        tmpW = W + y[errors[errorIndex]] * X[errorIndex]\n",
    "        # 获取临时权重系数下错误点的下标集合\n",
    "        tmpErrors = errorIndexes(tmpW, X, y)\n",
    "        # 如果错误点数量更少，就更新权重系数\n",
    "        if len(errors) >= len(tmpErrors):\n",
    "            iterNoChange = 0\n",
    "            # 修正权重系数\n",
    "            W = tmpW\n",
    "            errors = tmpErrors\n",
    "        if iterNoChange >= maxIterNoChange:\n",
    "            break\n",
    "    return W"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "计算权重系数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 4.65320509, 13.86100871])"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "W = pocket(X, y, 50, 20)\n",
    "W"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可视化："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "plt.rcParams['font.sans-serif'] = ['PingFang HK']  # 选择一个本地的支持中文的字体\n",
    "\n",
    "# %matplotlib inline\n",
    "\n",
    "x1 = X[y>0][:, 0]\n",
    "y1 = X[y>0][:, 1]\n",
    "x2 = X[y<0][:, 0]\n",
    "y2 = X[y<0][:, 1]\n",
    "plt.title('口袋算法')\n",
    "plt.scatter(x1, y1, c='b', marker='o', s=20)\n",
    "plt.scatter(x2, y2, c='r', marker='o', s=20)\n",
    "x3, y3 = buildLine(W, start, end)\n",
    "plt.plot(x3, y3)\n",
    "plt.legend([p1, p2], [\"正1\", \"负1\"], loc=\"upper right\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用 scikit-learn 拟合："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([2.71448776, 3.84548235])"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sklearn.linear_model import Perceptron\n",
    "\n",
    "# 初始化感知器\n",
    "clf = Perceptron()\n",
    "# 用随机梯度下降拟合线性模型\n",
    "clf.fit(X, y)\n",
    "# 权重系数\n",
    "W = clf.coef_[0]\n",
    "W"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可视化："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "plt.rcParams['font.sans-serif'] = ['PingFang HK']  # 选择一个本地的支持中文的字体\n",
    "\n",
    "# %matplotlib inline\n",
    "\n",
    "x1 = X[y>0][:, 0]\n",
    "y1 = X[y>0][:, 1]\n",
    "x2 = X[y<0][:, 0]\n",
    "y2 = X[y<0][:, 1]\n",
    "plt.title('口袋算法')\n",
    "plt.scatter(x1, y1, c='b', marker='o', s=20)\n",
    "plt.scatter(x2, y2, c='r', marker='o', s=20)\n",
    "x3, y3 = buildLine(W, start, end)\n",
    "plt.plot(x3, y3)\n",
    "plt.legend([p1, p2], [\"正1\", \"负1\"], loc=\"upper right\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "def pocketReturn(X, y, iteration, maxIterNoChange = 5):\n",
    "    \"\"\"\n",
    "    口袋算法实现\n",
    "    args:\n",
    "        X - 训练数据集\n",
    "        y - 目标标签值\n",
    "        iteration - 最大迭代次数\n",
    "    return:\n",
    "        Ws - 权重系数数组\n",
    "        tmpWs - 临时权重系数数组\n",
    "        errorCounts - 权重系数错误数数组\n",
    "        tmpErrorCounts - 临时权重系数错误数数组\n",
    "    \"\"\"\n",
    "    np.random.seed(42)\n",
    "    # 初始化权重系数\n",
    "    W = np.zeros(X.shape[1])\n",
    "    Ws = np.array([W])\n",
    "    tmpWs = np.array([W])\n",
    "    # 获取错误点的下标集合\n",
    "    errors = errorIndexes(W, X, y)\n",
    "    errorCounts = np.array([len(errors)])\n",
    "    tmpErrorCounts = np.array([len(errors)])\n",
    "    iterNoChange = 0\n",
    "    # 循环\n",
    "    for i in range(iteration):\n",
    "        iterNoChange = iterNoChange + 1\n",
    "        # 随机获取错误点下标\n",
    "        errorIndex = np.random.randint(0, len(errors))\n",
    "        # 计算临时权重系数\n",
    "        tmpW = W + y[errors[errorIndex]] * X[errorIndex]\n",
    "        # 获取临时权重系数下错误点的下标集合\n",
    "        tmpErrors = errorIndexes(tmpW, X, y)\n",
    "        # 如果错误点数量更少，就更新权重系数\n",
    "        if len(errors) >= len(tmpErrors):\n",
    "            iterNoChange = 0\n",
    "            # 修正权重系数\n",
    "            W = tmpW\n",
    "            errors = tmpErrors\n",
    "        Ws = np.insert(Ws, len(Ws), values=W, axis=0)\n",
    "        tmpWs = np.insert(tmpWs, len(tmpWs), values=tmpW, axis=0)\n",
    "        errorCounts = np.insert(errorCounts, len(errorCounts), values=len(errors), axis=0)\n",
    "        tmpErrorCounts = np.insert(tmpErrorCounts, len(tmpErrorCounts), values=len(tmpErrors), axis=0)\n",
    "        if iterNoChange >= maxIterNoChange:\n",
    "            break\n",
    "    return Ws, tmpWs, errorCounts, tmpErrorCounts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "初始化复杂带噪声的数据集："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 坐标轴起始点\n",
    "start = -10\n",
    "# 坐标轴结束点\n",
    "end = 10\n",
    "# 目标权重系数\n",
    "W = np.array([2, 5])\n",
    "# 创建线性可分的训练数据集\n",
    "X, y = createTrainDatasWithNoise(W, start, end, 1000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "计算权重系数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 5.92091626, 15.47423216])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "W = pocket(X, y, 50, 20)\n",
    "W"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可视化："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "plt.rcParams['font.sans-serif'] = ['PingFang HK']  # 选择一个本地的支持中文的字体\n",
    "\n",
    "# %matplotlib inline\n",
    "\n",
    "x1 = X[y>0][:, 0]\n",
    "y1 = X[y>0][:, 1]\n",
    "x2 = X[y<0][:, 0]\n",
    "y2 = X[y<0][:, 1]\n",
    "plt.title('口袋算法')\n",
    "p1 = plt.scatter(x1, y1, c='b', marker='o', s=20)\n",
    "p2 = plt.scatter(x2, y2, c='r', marker='o', s=20)\n",
    "x3, y3 = buildLine(W, start, end)\n",
    "plt.plot(x3, y3)\n",
    "plt.legend([p1, p2], [\"正1\", \"负1\"], loc=\"upper right\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用 scikit-learn 拟合："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 6.43384469, 14.98483091])"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sklearn.linear_model import Perceptron\n",
    "\n",
    "# 初始化感知器\n",
    "clf = Perceptron(max_iter = 50, n_iter_no_change = 10)\n",
    "# 用随机梯度下降拟合线性模型\n",
    "clf.fit(X, y)\n",
    "# 权重系数\n",
    "W = clf.coef_[0]\n",
    "W"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可视化："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "plt.rcParams['font.sans-serif'] = ['PingFang HK']  # 选择一个本地的支持中文的字体\n",
    "\n",
    "# %matplotlib inline\n",
    "\n",
    "x1 = X[y>0][:, 0]\n",
    "y1 = X[y>0][:, 1]\n",
    "x2 = X[y<0][:, 0]\n",
    "y2 = X[y<0][:, 1]\n",
    "plt.title('口袋算法')\n",
    "p1 = plt.scatter(x1, y1, c='b', marker='o', s=20)\n",
    "p2 = plt.scatter(x2, y2, c='r', marker='o', s=20)\n",
    "x3, y3 = buildLine(W, start, end)\n",
    "plt.plot(x3, y3)\n",
    "plt.legend([p1, p2], [\"正1\", \"负1\"], loc=\"upper right\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "动画演示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "/* global mpl */\n",
       "window.mpl = {};\n",
       "\n",
       "mpl.get_websocket_type = function () {\n",
       "    if (typeof WebSocket !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof MozWebSocket !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert(\n",
       "            'Your browser does not have WebSocket support. ' +\n",
       "                'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "                'Firefox 4 and 5 are also supported but you ' +\n",
       "                'have to enable WebSockets in about:config.'\n",
       "        );\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = this.ws.binaryType !== undefined;\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById('mpl-warnings');\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent =\n",
       "                'This browser does not support binary websocket messages. ' +\n",
       "                'Performance may be slow.';\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = document.createElement('div');\n",
       "    this.root.setAttribute('style', 'display: inline-block');\n",
       "    this._root_extra_style(this.root);\n",
       "\n",
       "    parent_element.appendChild(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen = function () {\n",
       "        fig.send_message('supports_binary', { value: fig.supports_binary });\n",
       "        fig.send_message('send_image_mode', {});\n",
       "        if (fig.ratio !== 1) {\n",
       "            fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n",
       "        }\n",
       "        fig.send_message('refresh', {});\n",
       "    };\n",
       "\n",
       "    this.imageObj.onload = function () {\n",
       "        if (fig.image_mode === 'full') {\n",
       "            // Full images could contain transparency (where diff images\n",
       "            // almost always do), so we need to clear the canvas so that\n",
       "            // there is no ghosting.\n",
       "            fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "        }\n",
       "        fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "    };\n",
       "\n",
       "    this.imageObj.onunload = function () {\n",
       "        fig.ws.close();\n",
       "    };\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._init_header = function () {\n",
       "    var titlebar = document.createElement('div');\n",
       "    titlebar.classList =\n",
       "        'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n",
       "    var titletext = document.createElement('div');\n",
       "    titletext.classList = 'ui-dialog-title';\n",
       "    titletext.setAttribute(\n",
       "        'style',\n",
       "        'width: 100%; text-align: center; padding: 3px;'\n",
       "    );\n",
       "    titlebar.appendChild(titletext);\n",
       "    this.root.appendChild(titlebar);\n",
       "    this.header = titletext;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function () {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = (this.canvas_div = document.createElement('div'));\n",
       "    canvas_div.setAttribute(\n",
       "        'style',\n",
       "        'border: 1px solid #ddd;' +\n",
       "            'box-sizing: content-box;' +\n",
       "            'clear: both;' +\n",
       "            'min-height: 1px;' +\n",
       "            'min-width: 1px;' +\n",
       "            'outline: 0;' +\n",
       "            'overflow: hidden;' +\n",
       "            'position: relative;' +\n",
       "            'resize: both;'\n",
       "    );\n",
       "\n",
       "    function on_keyboard_event_closure(name) {\n",
       "        return function (event) {\n",
       "            return fig.key_event(event, name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    canvas_div.addEventListener(\n",
       "        'keydown',\n",
       "        on_keyboard_event_closure('key_press')\n",
       "    );\n",
       "    canvas_div.addEventListener(\n",
       "        'keyup',\n",
       "        on_keyboard_event_closure('key_release')\n",
       "    );\n",
       "\n",
       "    this._canvas_extra_style(canvas_div);\n",
       "    this.root.appendChild(canvas_div);\n",
       "\n",
       "    var canvas = (this.canvas = document.createElement('canvas'));\n",
       "    canvas.classList.add('mpl-canvas');\n",
       "    canvas.setAttribute('style', 'box-sizing: content-box;');\n",
       "\n",
       "    this.context = canvas.getContext('2d');\n",
       "\n",
       "    var backingStore =\n",
       "        this.context.backingStorePixelRatio ||\n",
       "        this.context.webkitBackingStorePixelRatio ||\n",
       "        this.context.mozBackingStorePixelRatio ||\n",
       "        this.context.msBackingStorePixelRatio ||\n",
       "        this.context.oBackingStorePixelRatio ||\n",
       "        this.context.backingStorePixelRatio ||\n",
       "        1;\n",
       "\n",
       "    this.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n",
       "        'canvas'\n",
       "    ));\n",
       "    rubberband_canvas.setAttribute(\n",
       "        'style',\n",
       "        'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n",
       "    );\n",
       "\n",
       "    // Apply a ponyfill if ResizeObserver is not implemented by browser.\n",
       "    if (this.ResizeObserver === undefined) {\n",
       "        if (window.ResizeObserver !== undefined) {\n",
       "            this.ResizeObserver = window.ResizeObserver;\n",
       "        } else {\n",
       "            var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n",
       "            this.ResizeObserver = obs.ResizeObserver;\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n",
       "        var nentries = entries.length;\n",
       "        for (var i = 0; i < nentries; i++) {\n",
       "            var entry = entries[i];\n",
       "            var width, height;\n",
       "            if (entry.contentBoxSize) {\n",
       "                if (entry.contentBoxSize instanceof Array) {\n",
       "                    // Chrome 84 implements new version of spec.\n",
       "                    width = entry.contentBoxSize[0].inlineSize;\n",
       "                    height = entry.contentBoxSize[0].blockSize;\n",
       "                } else {\n",
       "                    // Firefox implements old version of spec.\n",
       "                    width = entry.contentBoxSize.inlineSize;\n",
       "                    height = entry.contentBoxSize.blockSize;\n",
       "                }\n",
       "            } else {\n",
       "                // Chrome <84 implements even older version of spec.\n",
       "                width = entry.contentRect.width;\n",
       "                height = entry.contentRect.height;\n",
       "            }\n",
       "\n",
       "            // Keep the size of the canvas and rubber band canvas in sync with\n",
       "            // the canvas container.\n",
       "            if (entry.devicePixelContentBoxSize) {\n",
       "                // Chrome 84 implements new version of spec.\n",
       "                canvas.setAttribute(\n",
       "                    'width',\n",
       "                    entry.devicePixelContentBoxSize[0].inlineSize\n",
       "                );\n",
       "                canvas.setAttribute(\n",
       "                    'height',\n",
       "                    entry.devicePixelContentBoxSize[0].blockSize\n",
       "                );\n",
       "            } else {\n",
       "                canvas.setAttribute('width', width * fig.ratio);\n",
       "                canvas.setAttribute('height', height * fig.ratio);\n",
       "            }\n",
       "            canvas.setAttribute(\n",
       "                'style',\n",
       "                'width: ' + width + 'px; height: ' + height + 'px;'\n",
       "            );\n",
       "\n",
       "            rubberband_canvas.setAttribute('width', width);\n",
       "            rubberband_canvas.setAttribute('height', height);\n",
       "\n",
       "            // And update the size in Python. We ignore the initial 0/0 size\n",
       "            // that occurs as the element is placed into the DOM, which should\n",
       "            // otherwise not happen due to the minimum size styling.\n",
       "            if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n",
       "                fig.request_resize(width, height);\n",
       "            }\n",
       "        }\n",
       "    });\n",
       "    this.resizeObserverInstance.observe(canvas_div);\n",
       "\n",
       "    function on_mouse_event_closure(name) {\n",
       "        return function (event) {\n",
       "            return fig.mouse_event(event, name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mousedown',\n",
       "        on_mouse_event_closure('button_press')\n",
       "    );\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseup',\n",
       "        on_mouse_event_closure('button_release')\n",
       "    );\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mousemove',\n",
       "        on_mouse_event_closure('motion_notify')\n",
       "    );\n",
       "\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseenter',\n",
       "        on_mouse_event_closure('figure_enter')\n",
       "    );\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseleave',\n",
       "        on_mouse_event_closure('figure_leave')\n",
       "    );\n",
       "\n",
       "    canvas_div.addEventListener('wheel', function (event) {\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        on_mouse_event_closure('scroll')(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.appendChild(canvas);\n",
       "    canvas_div.appendChild(rubberband_canvas);\n",
       "\n",
       "    this.rubberband_context = rubberband_canvas.getContext('2d');\n",
       "    this.rubberband_context.strokeStyle = '#000000';\n",
       "\n",
       "    this._resize_canvas = function (width, height, forward) {\n",
       "        if (forward) {\n",
       "            canvas_div.style.width = width + 'px';\n",
       "            canvas_div.style.height = height + 'px';\n",
       "        }\n",
       "    };\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n",
       "        event.preventDefault();\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus() {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function () {\n",
       "    var fig = this;\n",
       "\n",
       "    var toolbar = document.createElement('div');\n",
       "    toolbar.classList = 'mpl-toolbar';\n",
       "    this.root.appendChild(toolbar);\n",
       "\n",
       "    function on_click_closure(name) {\n",
       "        return function (_event) {\n",
       "            return fig.toolbar_button_onclick(name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    function on_mouseover_closure(tooltip) {\n",
       "        return function (event) {\n",
       "            if (!event.currentTarget.disabled) {\n",
       "                return fig.toolbar_button_onmouseover(tooltip);\n",
       "            }\n",
       "        };\n",
       "    }\n",
       "\n",
       "    fig.buttons = {};\n",
       "    var buttonGroup = document.createElement('div');\n",
       "    buttonGroup.classList = 'mpl-button-group';\n",
       "    for (var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            /* Instead of a spacer, we start a new button group. */\n",
       "            if (buttonGroup.hasChildNodes()) {\n",
       "                toolbar.appendChild(buttonGroup);\n",
       "            }\n",
       "            buttonGroup = document.createElement('div');\n",
       "            buttonGroup.classList = 'mpl-button-group';\n",
       "            continue;\n",
       "        }\n",
       "\n",
       "        var button = (fig.buttons[name] = document.createElement('button'));\n",
       "        button.classList = 'mpl-widget';\n",
       "        button.setAttribute('role', 'button');\n",
       "        button.setAttribute('aria-disabled', 'false');\n",
       "        button.addEventListener('click', on_click_closure(method_name));\n",
       "        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n",
       "\n",
       "        var icon_img = document.createElement('img');\n",
       "        icon_img.src = '_images/' + image + '.png';\n",
       "        icon_img.srcset = '_images/' + image + '_large.png 2x';\n",
       "        icon_img.alt = tooltip;\n",
       "        button.appendChild(icon_img);\n",
       "\n",
       "        buttonGroup.appendChild(button);\n",
       "    }\n",
       "\n",
       "    if (buttonGroup.hasChildNodes()) {\n",
       "        toolbar.appendChild(buttonGroup);\n",
       "    }\n",
       "\n",
       "    var fmt_picker = document.createElement('select');\n",
       "    fmt_picker.classList = 'mpl-widget';\n",
       "    toolbar.appendChild(fmt_picker);\n",
       "    this.format_dropdown = fmt_picker;\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = document.createElement('option');\n",
       "        option.selected = fmt === mpl.default_extension;\n",
       "        option.innerHTML = fmt;\n",
       "        fmt_picker.appendChild(option);\n",
       "    }\n",
       "\n",
       "    var status_bar = document.createElement('span');\n",
       "    status_bar.classList = 'mpl-message';\n",
       "    toolbar.appendChild(status_bar);\n",
       "    this.message = status_bar;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', { width: x_pixels, height: y_pixels });\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.send_message = function (type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function () {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_save = function (fig, _msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function (fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1], msg['forward']);\n",
       "        fig.send_message('refresh', {});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n",
       "    var x0 = msg['x0'] / fig.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n",
       "    var x1 = msg['x1'] / fig.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0,\n",
       "        0,\n",
       "        fig.canvas.width / fig.ratio,\n",
       "        fig.canvas.height / fig.ratio\n",
       "    );\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch (cursor) {\n",
       "        case 0:\n",
       "            cursor = 'pointer';\n",
       "            break;\n",
       "        case 1:\n",
       "            cursor = 'default';\n",
       "            break;\n",
       "        case 2:\n",
       "            cursor = 'crosshair';\n",
       "            break;\n",
       "        case 3:\n",
       "            cursor = 'move';\n",
       "            break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_message = function (fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n",
       "    for (var key in msg) {\n",
       "        if (!(key in fig.buttons)) {\n",
       "            continue;\n",
       "        }\n",
       "        fig.buttons[key].disabled = !msg[key];\n",
       "        fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n",
       "    if (msg['mode'] === 'PAN') {\n",
       "        fig.buttons['Pan'].classList.add('active');\n",
       "        fig.buttons['Zoom'].classList.remove('active');\n",
       "    } else if (msg['mode'] === 'ZOOM') {\n",
       "        fig.buttons['Pan'].classList.remove('active');\n",
       "        fig.buttons['Zoom'].classList.add('active');\n",
       "    } else {\n",
       "        fig.buttons['Pan'].classList.remove('active');\n",
       "        fig.buttons['Zoom'].classList.remove('active');\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function () {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message('ack', {});\n",
       "};\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function (fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = 'image/png';\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src\n",
       "                );\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data\n",
       "            );\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        } else if (\n",
       "            typeof evt.data === 'string' &&\n",
       "            evt.data.slice(0, 21) === 'data:image/png;base64'\n",
       "        ) {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig['handle_' + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\n",
       "                \"No handler for the '\" + msg_type + \"' message type: \",\n",
       "                msg\n",
       "            );\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\n",
       "                    \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n",
       "                    e,\n",
       "                    e.stack,\n",
       "                    msg\n",
       "                );\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "};\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function (e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e) {\n",
       "        e = window.event;\n",
       "    }\n",
       "    if (e.target) {\n",
       "        targ = e.target;\n",
       "    } else if (e.srcElement) {\n",
       "        targ = e.srcElement;\n",
       "    }\n",
       "    if (targ.nodeType === 3) {\n",
       "        // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "    }\n",
       "\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    var boundingRect = targ.getBoundingClientRect();\n",
       "    var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n",
       "    var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n",
       "\n",
       "    return { x: x, y: y };\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys(original) {\n",
       "    return Object.keys(original).reduce(function (obj, key) {\n",
       "        if (typeof original[key] !== 'object') {\n",
       "            obj[key] = original[key];\n",
       "        }\n",
       "        return obj;\n",
       "    }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function (event, name) {\n",
       "    var canvas_pos = mpl.findpos(event);\n",
       "\n",
       "    if (name === 'button_press') {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * this.ratio;\n",
       "    var y = canvas_pos.y * this.ratio;\n",
       "\n",
       "    this.send_message(name, {\n",
       "        x: x,\n",
       "        y: y,\n",
       "        button: event.button,\n",
       "        step: event.step,\n",
       "        guiEvent: simpleKeys(event),\n",
       "    });\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.key_event = function (event, name) {\n",
       "    // Prevent repeat events\n",
       "    if (name === 'key_press') {\n",
       "        if (event.which === this._key) {\n",
       "            return;\n",
       "        } else {\n",
       "            this._key = event.which;\n",
       "        }\n",
       "    }\n",
       "    if (name === 'key_release') {\n",
       "        this._key = null;\n",
       "    }\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which !== 17) {\n",
       "        value += 'ctrl+';\n",
       "    }\n",
       "    if (event.altKey && event.which !== 18) {\n",
       "        value += 'alt+';\n",
       "    }\n",
       "    if (event.shiftKey && event.which !== 16) {\n",
       "        value += 'shift+';\n",
       "    }\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n",
       "    return false;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n",
       "    if (name === 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message('toolbar_button', { name: name });\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "\n",
       "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n",
       "// prettier-ignore\n",
       "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";/* global mpl */\n",
       "\n",
       "var comm_websocket_adapter = function (comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function () {\n",
       "        comm.close();\n",
       "    };\n",
       "    ws.send = function (m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function (msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data']);\n",
       "    });\n",
       "    return ws;\n",
       "};\n",
       "\n",
       "mpl.mpl_figure_comm = function (comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = document.getElementById(id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm);\n",
       "\n",
       "    function ondownload(figure, _format) {\n",
       "        window.open(figure.canvas.toDataURL());\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element;\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error('Failed to find cell for figure', id, fig);\n",
       "        return;\n",
       "    }\n",
       "    fig.cell_info[0].output_area.element.on(\n",
       "        'cleared',\n",
       "        { fig: fig },\n",
       "        fig._remove_fig_handler\n",
       "    );\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function (fig, msg) {\n",
       "    var width = fig.canvas.width / fig.ratio;\n",
       "    fig.cell_info[0].output_area.element.off(\n",
       "        'cleared',\n",
       "        fig._remove_fig_handler\n",
       "    );\n",
       "    fig.resizeObserverInstance.unobserve(fig.canvas_div);\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable();\n",
       "    fig.parent_element.innerHTML =\n",
       "        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "    fig.close_ws(fig, msg);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.close_ws = function (fig, msg) {\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width / this.ratio;\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] =\n",
       "        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function () {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message('ack', {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () {\n",
       "        fig.push_to_output();\n",
       "    }, 1000);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function () {\n",
       "    var fig = this;\n",
       "\n",
       "    var toolbar = document.createElement('div');\n",
       "    toolbar.classList = 'btn-toolbar';\n",
       "    this.root.appendChild(toolbar);\n",
       "\n",
       "    function on_click_closure(name) {\n",
       "        return function (_event) {\n",
       "            return fig.toolbar_button_onclick(name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    function on_mouseover_closure(tooltip) {\n",
       "        return function (event) {\n",
       "            if (!event.currentTarget.disabled) {\n",
       "                return fig.toolbar_button_onmouseover(tooltip);\n",
       "            }\n",
       "        };\n",
       "    }\n",
       "\n",
       "    fig.buttons = {};\n",
       "    var buttonGroup = document.createElement('div');\n",
       "    buttonGroup.classList = 'btn-group';\n",
       "    var button;\n",
       "    for (var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            /* Instead of a spacer, we start a new button group. */\n",
       "            if (buttonGroup.hasChildNodes()) {\n",
       "                toolbar.appendChild(buttonGroup);\n",
       "            }\n",
       "            buttonGroup = document.createElement('div');\n",
       "            buttonGroup.classList = 'btn-group';\n",
       "            continue;\n",
       "        }\n",
       "\n",
       "        button = fig.buttons[name] = document.createElement('button');\n",
       "        button.classList = 'btn btn-default';\n",
       "        button.href = '#';\n",
       "        button.title = name;\n",
       "        button.innerHTML = '<i class=\"fa ' + image + ' fa-lg\"></i>';\n",
       "        button.addEventListener('click', on_click_closure(method_name));\n",
       "        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n",
       "        buttonGroup.appendChild(button);\n",
       "    }\n",
       "\n",
       "    if (buttonGroup.hasChildNodes()) {\n",
       "        toolbar.appendChild(buttonGroup);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = document.createElement('span');\n",
       "    status_bar.classList = 'mpl-message pull-right';\n",
       "    toolbar.appendChild(status_bar);\n",
       "    this.message = status_bar;\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = document.createElement('div');\n",
       "    buttongrp.classList = 'btn-group inline pull-right';\n",
       "    button = document.createElement('button');\n",
       "    button.classList = 'btn btn-mini btn-primary';\n",
       "    button.href = '#';\n",
       "    button.title = 'Stop Interaction';\n",
       "    button.innerHTML = '<i class=\"fa fa-power-off icon-remove icon-large\"></i>';\n",
       "    button.addEventListener('click', function (_evt) {\n",
       "        fig.handle_close(fig, {});\n",
       "    });\n",
       "    button.addEventListener(\n",
       "        'mouseover',\n",
       "        on_mouseover_closure('Stop Interaction')\n",
       "    );\n",
       "    buttongrp.appendChild(button);\n",
       "    var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n",
       "    titlebar.insertBefore(buttongrp, titlebar.firstChild);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._remove_fig_handler = function (event) {\n",
       "    var fig = event.data.fig;\n",
       "    if (event.target !== this) {\n",
       "        // Ignore bubbled events from children.\n",
       "        return;\n",
       "    }\n",
       "    fig.close_ws(fig, {});\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function (el) {\n",
       "    el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function (el) {\n",
       "    // this is important to make the div 'focusable\n",
       "    el.setAttribute('tabindex', 0);\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    } else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function (event, _name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager) {\n",
       "        manager = IPython.keyboard_manager;\n",
       "    }\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which === 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_save = function (fig, _msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "};\n",
       "\n",
       "mpl.find_output_cell = function (html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i = 0; i < ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code') {\n",
       "            for (var j = 0; j < cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] === html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "};\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel !== null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target(\n",
       "        'matplotlib',\n",
       "        mpl.mpl_figure_comm\n",
       "    );\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"\" width=\"640\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.animation as animation\n",
    "\n",
    "plt.rcParams['font.sans-serif'] = ['PingFang HK']  # 选择一个本地的支持中文的字体\n",
    "\n",
    "%matplotlib notebook\n",
    "\n",
    "start = -10\n",
    "end = 10\n",
    "W = np.array([2, 5])\n",
    "X, y = createTrainDatasWithNoise(W, start, end)\n",
    "x1 = X[y>0][:, 0]\n",
    "y1 = X[y>0][:, 1]\n",
    "x2 = X[y<0][:, 0]\n",
    "y2 = X[y<0][:, 1]\n",
    "\n",
    "Ws, tmpWs, errorCounts, tmpErrorCounts = pocketReturn(X, y, 50)\n",
    "fig = plt.figure()\n",
    "plt.title('口袋算法')\n",
    "plt.scatter(x1, y1, c='b', marker='o', s=20)\n",
    "plt.scatter(x2, y2, c='r', marker='o', s=20)\n",
    "line, = plt.plot(0, 0, markersize=0, label='W')\n",
    "tmpLine, = plt.plot(0, 0, markersize=0, label='Temp W')\n",
    "\n",
    "def update(i):\n",
    "    x3, y3 = buildLine(Ws[i], start, end)\n",
    "    x4, y4 = buildLine(tmpWs[i], start, end)\n",
    "    line.set_data(x3, y3)\n",
    "    line.set_label(\"W: %d 个错误点\"%(errorCounts[i]))\n",
    "    tmpLine.set_data(x4, y4)\n",
    "    tmpLine.set_label(\"Temp W: %d 个错误点\"%(tmpErrorCounts[i]))\n",
    "    plt.legend(loc=\"upper right\")\n",
    "    return line, tmpLine,\n",
    "\n",
    "ani = animation.FuncAnimation(fig, update, range(0, len(Ws)), interval=1000, blit=True, repeat=False)\n",
    "ani.save('pocket_simple.gif')\n",
    "plt.legend(loc=\"upper right\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "动画演示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "/* global mpl */\n",
       "window.mpl = {};\n",
       "\n",
       "mpl.get_websocket_type = function () {\n",
       "    if (typeof WebSocket !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof MozWebSocket !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert(\n",
       "            'Your browser does not have WebSocket support. ' +\n",
       "                'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "                'Firefox 4 and 5 are also supported but you ' +\n",
       "                'have to enable WebSockets in about:config.'\n",
       "        );\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = this.ws.binaryType !== undefined;\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById('mpl-warnings');\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent =\n",
       "                'This browser does not support binary websocket messages. ' +\n",
       "                'Performance may be slow.';\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = document.createElement('div');\n",
       "    this.root.setAttribute('style', 'display: inline-block');\n",
       "    this._root_extra_style(this.root);\n",
       "\n",
       "    parent_element.appendChild(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen = function () {\n",
       "        fig.send_message('supports_binary', { value: fig.supports_binary });\n",
       "        fig.send_message('send_image_mode', {});\n",
       "        if (fig.ratio !== 1) {\n",
       "            fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n",
       "        }\n",
       "        fig.send_message('refresh', {});\n",
       "    };\n",
       "\n",
       "    this.imageObj.onload = function () {\n",
       "        if (fig.image_mode === 'full') {\n",
       "            // Full images could contain transparency (where diff images\n",
       "            // almost always do), so we need to clear the canvas so that\n",
       "            // there is no ghosting.\n",
       "            fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "        }\n",
       "        fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "    };\n",
       "\n",
       "    this.imageObj.onunload = function () {\n",
       "        fig.ws.close();\n",
       "    };\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._init_header = function () {\n",
       "    var titlebar = document.createElement('div');\n",
       "    titlebar.classList =\n",
       "        'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n",
       "    var titletext = document.createElement('div');\n",
       "    titletext.classList = 'ui-dialog-title';\n",
       "    titletext.setAttribute(\n",
       "        'style',\n",
       "        'width: 100%; text-align: center; padding: 3px;'\n",
       "    );\n",
       "    titlebar.appendChild(titletext);\n",
       "    this.root.appendChild(titlebar);\n",
       "    this.header = titletext;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function () {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = (this.canvas_div = document.createElement('div'));\n",
       "    canvas_div.setAttribute(\n",
       "        'style',\n",
       "        'border: 1px solid #ddd;' +\n",
       "            'box-sizing: content-box;' +\n",
       "            'clear: both;' +\n",
       "            'min-height: 1px;' +\n",
       "            'min-width: 1px;' +\n",
       "            'outline: 0;' +\n",
       "            'overflow: hidden;' +\n",
       "            'position: relative;' +\n",
       "            'resize: both;'\n",
       "    );\n",
       "\n",
       "    function on_keyboard_event_closure(name) {\n",
       "        return function (event) {\n",
       "            return fig.key_event(event, name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    canvas_div.addEventListener(\n",
       "        'keydown',\n",
       "        on_keyboard_event_closure('key_press')\n",
       "    );\n",
       "    canvas_div.addEventListener(\n",
       "        'keyup',\n",
       "        on_keyboard_event_closure('key_release')\n",
       "    );\n",
       "\n",
       "    this._canvas_extra_style(canvas_div);\n",
       "    this.root.appendChild(canvas_div);\n",
       "\n",
       "    var canvas = (this.canvas = document.createElement('canvas'));\n",
       "    canvas.classList.add('mpl-canvas');\n",
       "    canvas.setAttribute('style', 'box-sizing: content-box;');\n",
       "\n",
       "    this.context = canvas.getContext('2d');\n",
       "\n",
       "    var backingStore =\n",
       "        this.context.backingStorePixelRatio ||\n",
       "        this.context.webkitBackingStorePixelRatio ||\n",
       "        this.context.mozBackingStorePixelRatio ||\n",
       "        this.context.msBackingStorePixelRatio ||\n",
       "        this.context.oBackingStorePixelRatio ||\n",
       "        this.context.backingStorePixelRatio ||\n",
       "        1;\n",
       "\n",
       "    this.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n",
       "        'canvas'\n",
       "    ));\n",
       "    rubberband_canvas.setAttribute(\n",
       "        'style',\n",
       "        'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n",
       "    );\n",
       "\n",
       "    // Apply a ponyfill if ResizeObserver is not implemented by browser.\n",
       "    if (this.ResizeObserver === undefined) {\n",
       "        if (window.ResizeObserver !== undefined) {\n",
       "            this.ResizeObserver = window.ResizeObserver;\n",
       "        } else {\n",
       "            var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n",
       "            this.ResizeObserver = obs.ResizeObserver;\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n",
       "        var nentries = entries.length;\n",
       "        for (var i = 0; i < nentries; i++) {\n",
       "            var entry = entries[i];\n",
       "            var width, height;\n",
       "            if (entry.contentBoxSize) {\n",
       "                if (entry.contentBoxSize instanceof Array) {\n",
       "                    // Chrome 84 implements new version of spec.\n",
       "                    width = entry.contentBoxSize[0].inlineSize;\n",
       "                    height = entry.contentBoxSize[0].blockSize;\n",
       "                } else {\n",
       "                    // Firefox implements old version of spec.\n",
       "                    width = entry.contentBoxSize.inlineSize;\n",
       "                    height = entry.contentBoxSize.blockSize;\n",
       "                }\n",
       "            } else {\n",
       "                // Chrome <84 implements even older version of spec.\n",
       "                width = entry.contentRect.width;\n",
       "                height = entry.contentRect.height;\n",
       "            }\n",
       "\n",
       "            // Keep the size of the canvas and rubber band canvas in sync with\n",
       "            // the canvas container.\n",
       "            if (entry.devicePixelContentBoxSize) {\n",
       "                // Chrome 84 implements new version of spec.\n",
       "                canvas.setAttribute(\n",
       "                    'width',\n",
       "                    entry.devicePixelContentBoxSize[0].inlineSize\n",
       "                );\n",
       "                canvas.setAttribute(\n",
       "                    'height',\n",
       "                    entry.devicePixelContentBoxSize[0].blockSize\n",
       "                );\n",
       "            } else {\n",
       "                canvas.setAttribute('width', width * fig.ratio);\n",
       "                canvas.setAttribute('height', height * fig.ratio);\n",
       "            }\n",
       "            canvas.setAttribute(\n",
       "                'style',\n",
       "                'width: ' + width + 'px; height: ' + height + 'px;'\n",
       "            );\n",
       "\n",
       "            rubberband_canvas.setAttribute('width', width);\n",
       "            rubberband_canvas.setAttribute('height', height);\n",
       "\n",
       "            // And update the size in Python. We ignore the initial 0/0 size\n",
       "            // that occurs as the element is placed into the DOM, which should\n",
       "            // otherwise not happen due to the minimum size styling.\n",
       "            if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n",
       "                fig.request_resize(width, height);\n",
       "            }\n",
       "        }\n",
       "    });\n",
       "    this.resizeObserverInstance.observe(canvas_div);\n",
       "\n",
       "    function on_mouse_event_closure(name) {\n",
       "        return function (event) {\n",
       "            return fig.mouse_event(event, name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mousedown',\n",
       "        on_mouse_event_closure('button_press')\n",
       "    );\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseup',\n",
       "        on_mouse_event_closure('button_release')\n",
       "    );\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mousemove',\n",
       "        on_mouse_event_closure('motion_notify')\n",
       "    );\n",
       "\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseenter',\n",
       "        on_mouse_event_closure('figure_enter')\n",
       "    );\n",
       "    rubberband_canvas.addEventListener(\n",
       "        'mouseleave',\n",
       "        on_mouse_event_closure('figure_leave')\n",
       "    );\n",
       "\n",
       "    canvas_div.addEventListener('wheel', function (event) {\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        on_mouse_event_closure('scroll')(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.appendChild(canvas);\n",
       "    canvas_div.appendChild(rubberband_canvas);\n",
       "\n",
       "    this.rubberband_context = rubberband_canvas.getContext('2d');\n",
       "    this.rubberband_context.strokeStyle = '#000000';\n",
       "\n",
       "    this._resize_canvas = function (width, height, forward) {\n",
       "        if (forward) {\n",
       "            canvas_div.style.width = width + 'px';\n",
       "            canvas_div.style.height = height + 'px';\n",
       "        }\n",
       "    };\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n",
       "        event.preventDefault();\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus() {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function () {\n",
       "    var fig = this;\n",
       "\n",
       "    var toolbar = document.createElement('div');\n",
       "    toolbar.classList = 'mpl-toolbar';\n",
       "    this.root.appendChild(toolbar);\n",
       "\n",
       "    function on_click_closure(name) {\n",
       "        return function (_event) {\n",
       "            return fig.toolbar_button_onclick(name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    function on_mouseover_closure(tooltip) {\n",
       "        return function (event) {\n",
       "            if (!event.currentTarget.disabled) {\n",
       "                return fig.toolbar_button_onmouseover(tooltip);\n",
       "            }\n",
       "        };\n",
       "    }\n",
       "\n",
       "    fig.buttons = {};\n",
       "    var buttonGroup = document.createElement('div');\n",
       "    buttonGroup.classList = 'mpl-button-group';\n",
       "    for (var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            /* Instead of a spacer, we start a new button group. */\n",
       "            if (buttonGroup.hasChildNodes()) {\n",
       "                toolbar.appendChild(buttonGroup);\n",
       "            }\n",
       "            buttonGroup = document.createElement('div');\n",
       "            buttonGroup.classList = 'mpl-button-group';\n",
       "            continue;\n",
       "        }\n",
       "\n",
       "        var button = (fig.buttons[name] = document.createElement('button'));\n",
       "        button.classList = 'mpl-widget';\n",
       "        button.setAttribute('role', 'button');\n",
       "        button.setAttribute('aria-disabled', 'false');\n",
       "        button.addEventListener('click', on_click_closure(method_name));\n",
       "        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n",
       "\n",
       "        var icon_img = document.createElement('img');\n",
       "        icon_img.src = '_images/' + image + '.png';\n",
       "        icon_img.srcset = '_images/' + image + '_large.png 2x';\n",
       "        icon_img.alt = tooltip;\n",
       "        button.appendChild(icon_img);\n",
       "\n",
       "        buttonGroup.appendChild(button);\n",
       "    }\n",
       "\n",
       "    if (buttonGroup.hasChildNodes()) {\n",
       "        toolbar.appendChild(buttonGroup);\n",
       "    }\n",
       "\n",
       "    var fmt_picker = document.createElement('select');\n",
       "    fmt_picker.classList = 'mpl-widget';\n",
       "    toolbar.appendChild(fmt_picker);\n",
       "    this.format_dropdown = fmt_picker;\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = document.createElement('option');\n",
       "        option.selected = fmt === mpl.default_extension;\n",
       "        option.innerHTML = fmt;\n",
       "        fmt_picker.appendChild(option);\n",
       "    }\n",
       "\n",
       "    var status_bar = document.createElement('span');\n",
       "    status_bar.classList = 'mpl-message';\n",
       "    toolbar.appendChild(status_bar);\n",
       "    this.message = status_bar;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', { width: x_pixels, height: y_pixels });\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.send_message = function (type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function () {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_save = function (fig, _msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function (fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1], msg['forward']);\n",
       "        fig.send_message('refresh', {});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n",
       "    var x0 = msg['x0'] / fig.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n",
       "    var x1 = msg['x1'] / fig.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0,\n",
       "        0,\n",
       "        fig.canvas.width / fig.ratio,\n",
       "        fig.canvas.height / fig.ratio\n",
       "    );\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch (cursor) {\n",
       "        case 0:\n",
       "            cursor = 'pointer';\n",
       "            break;\n",
       "        case 1:\n",
       "            cursor = 'default';\n",
       "            break;\n",
       "        case 2:\n",
       "            cursor = 'crosshair';\n",
       "            break;\n",
       "        case 3:\n",
       "            cursor = 'move';\n",
       "            break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_message = function (fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n",
       "    for (var key in msg) {\n",
       "        if (!(key in fig.buttons)) {\n",
       "            continue;\n",
       "        }\n",
       "        fig.buttons[key].disabled = !msg[key];\n",
       "        fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n",
       "    if (msg['mode'] === 'PAN') {\n",
       "        fig.buttons['Pan'].classList.add('active');\n",
       "        fig.buttons['Zoom'].classList.remove('active');\n",
       "    } else if (msg['mode'] === 'ZOOM') {\n",
       "        fig.buttons['Pan'].classList.remove('active');\n",
       "        fig.buttons['Zoom'].classList.add('active');\n",
       "    } else {\n",
       "        fig.buttons['Pan'].classList.remove('active');\n",
       "        fig.buttons['Zoom'].classList.remove('active');\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function () {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message('ack', {});\n",
       "};\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function (fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = 'image/png';\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src\n",
       "                );\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data\n",
       "            );\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        } else if (\n",
       "            typeof evt.data === 'string' &&\n",
       "            evt.data.slice(0, 21) === 'data:image/png;base64'\n",
       "        ) {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig['handle_' + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\n",
       "                \"No handler for the '\" + msg_type + \"' message type: \",\n",
       "                msg\n",
       "            );\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\n",
       "                    \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n",
       "                    e,\n",
       "                    e.stack,\n",
       "                    msg\n",
       "                );\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "};\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function (e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e) {\n",
       "        e = window.event;\n",
       "    }\n",
       "    if (e.target) {\n",
       "        targ = e.target;\n",
       "    } else if (e.srcElement) {\n",
       "        targ = e.srcElement;\n",
       "    }\n",
       "    if (targ.nodeType === 3) {\n",
       "        // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "    }\n",
       "\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    var boundingRect = targ.getBoundingClientRect();\n",
       "    var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n",
       "    var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n",
       "\n",
       "    return { x: x, y: y };\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys(original) {\n",
       "    return Object.keys(original).reduce(function (obj, key) {\n",
       "        if (typeof original[key] !== 'object') {\n",
       "            obj[key] = original[key];\n",
       "        }\n",
       "        return obj;\n",
       "    }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function (event, name) {\n",
       "    var canvas_pos = mpl.findpos(event);\n",
       "\n",
       "    if (name === 'button_press') {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * this.ratio;\n",
       "    var y = canvas_pos.y * this.ratio;\n",
       "\n",
       "    this.send_message(name, {\n",
       "        x: x,\n",
       "        y: y,\n",
       "        button: event.button,\n",
       "        step: event.step,\n",
       "        guiEvent: simpleKeys(event),\n",
       "    });\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.key_event = function (event, name) {\n",
       "    // Prevent repeat events\n",
       "    if (name === 'key_press') {\n",
       "        if (event.which === this._key) {\n",
       "            return;\n",
       "        } else {\n",
       "            this._key = event.which;\n",
       "        }\n",
       "    }\n",
       "    if (name === 'key_release') {\n",
       "        this._key = null;\n",
       "    }\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which !== 17) {\n",
       "        value += 'ctrl+';\n",
       "    }\n",
       "    if (event.altKey && event.which !== 18) {\n",
       "        value += 'alt+';\n",
       "    }\n",
       "    if (event.shiftKey && event.which !== 16) {\n",
       "        value += 'shift+';\n",
       "    }\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n",
       "    return false;\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n",
       "    if (name === 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message('toolbar_button', { name: name });\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "\n",
       "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n",
       "// prettier-ignore\n",
       "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";/* global mpl */\n",
       "\n",
       "var comm_websocket_adapter = function (comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function () {\n",
       "        comm.close();\n",
       "    };\n",
       "    ws.send = function (m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function (msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overridden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data']);\n",
       "    });\n",
       "    return ws;\n",
       "};\n",
       "\n",
       "mpl.mpl_figure_comm = function (comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = document.getElementById(id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm);\n",
       "\n",
       "    function ondownload(figure, _format) {\n",
       "        window.open(figure.canvas.toDataURL());\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element;\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error('Failed to find cell for figure', id, fig);\n",
       "        return;\n",
       "    }\n",
       "    fig.cell_info[0].output_area.element.on(\n",
       "        'cleared',\n",
       "        { fig: fig },\n",
       "        fig._remove_fig_handler\n",
       "    );\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function (fig, msg) {\n",
       "    var width = fig.canvas.width / fig.ratio;\n",
       "    fig.cell_info[0].output_area.element.off(\n",
       "        'cleared',\n",
       "        fig._remove_fig_handler\n",
       "    );\n",
       "    fig.resizeObserverInstance.unobserve(fig.canvas_div);\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable();\n",
       "    fig.parent_element.innerHTML =\n",
       "        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "    fig.close_ws(fig, msg);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.close_ws = function (fig, msg) {\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width / this.ratio;\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] =\n",
       "        '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function () {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message('ack', {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () {\n",
       "        fig.push_to_output();\n",
       "    }, 1000);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function () {\n",
       "    var fig = this;\n",
       "\n",
       "    var toolbar = document.createElement('div');\n",
       "    toolbar.classList = 'btn-toolbar';\n",
       "    this.root.appendChild(toolbar);\n",
       "\n",
       "    function on_click_closure(name) {\n",
       "        return function (_event) {\n",
       "            return fig.toolbar_button_onclick(name);\n",
       "        };\n",
       "    }\n",
       "\n",
       "    function on_mouseover_closure(tooltip) {\n",
       "        return function (event) {\n",
       "            if (!event.currentTarget.disabled) {\n",
       "                return fig.toolbar_button_onmouseover(tooltip);\n",
       "            }\n",
       "        };\n",
       "    }\n",
       "\n",
       "    fig.buttons = {};\n",
       "    var buttonGroup = document.createElement('div');\n",
       "    buttonGroup.classList = 'btn-group';\n",
       "    var button;\n",
       "    for (var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            /* Instead of a spacer, we start a new button group. */\n",
       "            if (buttonGroup.hasChildNodes()) {\n",
       "                toolbar.appendChild(buttonGroup);\n",
       "            }\n",
       "            buttonGroup = document.createElement('div');\n",
       "            buttonGroup.classList = 'btn-group';\n",
       "            continue;\n",
       "        }\n",
       "\n",
       "        button = fig.buttons[name] = document.createElement('button');\n",
       "        button.classList = 'btn btn-default';\n",
       "        button.href = '#';\n",
       "        button.title = name;\n",
       "        button.innerHTML = '<i class=\"fa ' + image + ' fa-lg\"></i>';\n",
       "        button.addEventListener('click', on_click_closure(method_name));\n",
       "        button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n",
       "        buttonGroup.appendChild(button);\n",
       "    }\n",
       "\n",
       "    if (buttonGroup.hasChildNodes()) {\n",
       "        toolbar.appendChild(buttonGroup);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = document.createElement('span');\n",
       "    status_bar.classList = 'mpl-message pull-right';\n",
       "    toolbar.appendChild(status_bar);\n",
       "    this.message = status_bar;\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = document.createElement('div');\n",
       "    buttongrp.classList = 'btn-group inline pull-right';\n",
       "    button = document.createElement('button');\n",
       "    button.classList = 'btn btn-mini btn-primary';\n",
       "    button.href = '#';\n",
       "    button.title = 'Stop Interaction';\n",
       "    button.innerHTML = '<i class=\"fa fa-power-off icon-remove icon-large\"></i>';\n",
       "    button.addEventListener('click', function (_evt) {\n",
       "        fig.handle_close(fig, {});\n",
       "    });\n",
       "    button.addEventListener(\n",
       "        'mouseover',\n",
       "        on_mouseover_closure('Stop Interaction')\n",
       "    );\n",
       "    buttongrp.appendChild(button);\n",
       "    var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n",
       "    titlebar.insertBefore(buttongrp, titlebar.firstChild);\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._remove_fig_handler = function (event) {\n",
       "    var fig = event.data.fig;\n",
       "    if (event.target !== this) {\n",
       "        // Ignore bubbled events from children.\n",
       "        return;\n",
       "    }\n",
       "    fig.close_ws(fig, {});\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function (el) {\n",
       "    el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function (el) {\n",
       "    // this is important to make the div 'focusable\n",
       "    el.setAttribute('tabindex', 0);\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    } else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function (event, _name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager) {\n",
       "        manager = IPython.keyboard_manager;\n",
       "    }\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which === 13) {\n",
       "        this.canvas_div.blur();\n",
       "        // select the cell after this one\n",
       "        var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n",
       "        IPython.notebook.select(index + 1);\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_save = function (fig, _msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "};\n",
       "\n",
       "mpl.find_output_cell = function (html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i = 0; i < ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code') {\n",
       "            for (var j = 0; j < cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] === html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "};\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel !== null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target(\n",
       "        'matplotlib',\n",
       "        mpl.mpl_figure_comm\n",
       "    );\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"\" width=\"640\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.animation as animation\n",
    "\n",
    "plt.rcParams['font.sans-serif'] = ['PingFang HK']  # 选择一个本地的支持中文的字体\n",
    "\n",
    "%matplotlib notebook\n",
    "\n",
    "start = -10\n",
    "end = 10\n",
    "W = np.array([2, 5])\n",
    "X, y = createTrainDatasWithNoise(W, start, end, 1000)\n",
    "x1 = X[y>0][:, 0]\n",
    "y1 = X[y>0][:, 1]\n",
    "x2 = X[y<0][:, 0]\n",
    "y2 = X[y<0][:, 1]\n",
    "\n",
    "Ws, tmpWs, errorCounts, tmpErrorCounts = pocketReturn(X, y, 50, 20)\n",
    "fig = plt.figure()\n",
    "plt.title('口袋算法')\n",
    "plt.scatter(x1, y1, c='b', marker='o', s=20)\n",
    "plt.scatter(x2, y2, c='r', marker='o', s=20)\n",
    "line, = plt.plot(0, 0, markersize=0, label='W')\n",
    "tmpLine, = plt.plot(0, 0, markersize=0, label='Temp W')\n",
    "\n",
    "def update(i):\n",
    "    x3, y3 = buildLine(Ws[i], start, end)\n",
    "    x4, y4 = buildLine(tmpWs[i], start, end)\n",
    "    line.set_data(x3, y3)\n",
    "    line.set_label(\"W: %d 个错误点\"%(errorCounts[i]))\n",
    "    tmpLine.set_data(x4, y4)\n",
    "    tmpLine.set_label(\"Temp W: %d 个错误点\"%(tmpErrorCounts[i]))\n",
    "    plt.legend(loc=\"upper right\")\n",
    "    return line, tmpLine,\n",
    "\n",
    "ani = animation.FuncAnimation(fig, update, range(0, len(Ws)), interval=1000, blit=True, repeat=False)\n",
    "ani.save('pocket.gif')\n",
    "plt.legend(loc=\"upper right\")\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
